package com.sora.utils;

public class SnowflakeIdGenerator {

    // 起始的时间戳，可以设置为你的项目上线时间，之后不会再往前推
    // 毫秒级时间戳，这里设置为 2020-01-01 00:00:00 (GMT+8)
    private final long twepoch = 1577836800000L;

    // 机器ID所占的位数 (5位，最大支持31台机器)
    private final long workerIdBits = 5L;
    // 数据中心ID所占的位数 (5位，最大支持31个数据中心)
    private final long datacenterIdBits = 5L;
    // 序列号所占的位数 (12位，每毫秒支持4095个序列号)
    private final long sequenceBits = 12L;

    // 机器ID向左移12位
    private final long workerIdShift = sequenceBits;
    // 数据中心ID向左移17位 (12+5)
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间戳向左移22位 (12+5+5)
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    // 生成序列的掩码 (最大序列号，也就是4095)
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 工作机器ID (0~31)
    private long workerId;
    // 数据中心ID (0~31)
    private long datacenterId;
    // 毫秒内序列 (0~4095)
    private long sequence = 0L;
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;

    private static final SnowflakeIdGenerator snowflakeIdGenerator = new SnowflakeIdGenerator(0, 0);

    /**
     * 构造函数
     *
     * @param workerId     工作机器ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        // 检查参数是否合法
        long maxWorkerId = -1L ^ (-1L << workerIdBits);
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("工作机器ID不能大于 %d 或小于 0", maxWorkerId)); // 中文错误信息
        }

        long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("数据中心ID不能大于 %d 或小于 0", maxDatacenterId)); // 中文错误信息
        }

        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 生成下一个 ID
     *
     * @return 下一个雪花 ID（Long 类型）
     */
    public synchronized long nextId() {
        long timestamp = timeGen(); // 获取当前时间戳

        // 如果当前时间小于上次生成ID的时间戳，说明系统时钟回退，抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("系统时钟回退。拒绝为过去 %d 毫秒生成ID", lastTimestamp - timestamp)); // 中文错误信息
        }

        // 如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask; // 序列号递增
            // 毫秒内序列溢出，等待下一毫秒
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变，毫秒内序列重置
            sequence = 0L;
        }

        // 更新上次生成ID的时间戳
        lastTimestamp = timestamp;

        // 拼接成最终的 ID
        return ((timestamp - twepoch) << timestampLeftShift) // 时间戳部分
                | (datacenterId << datacenterIdShift)         // 数据中心 ID 部分
                | (workerId << workerIdShift)                 // 机器 ID 部分
                | sequence;                                   // 序列号部分
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 获取当前时间戳（毫秒）
     *
     * @return 当前时间戳
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    // Hutool 的 getSnowflakeNextIdStr() 返回的是字符串，因此你可能需要转换为字符串
    public static String nextIdStr() {
        return String.valueOf(snowflakeIdGenerator.nextId());
    }
}